﻿/* 
 *  <copyright file="GamepadDevice.cs" company="Wesley Jowitt">
 *  
 *  SobrietyEngine: Copyright (c) 2011 Wesley Jowitt
 *  
 *  This software is distributed under a software license.
 *   * As of the creation time of this file, the license in use is the Microsoft Permissive License (Ms-PL) 
 *    * The license can be viewed at:
 *     * License.Txt in the root folder of this project
 *      * http://sobrietyengine.codeplex.com/license
 *       * http://www.microsoft.com/opensource/licenses.mspx
 *       
 *  </copyright>
 *   */

using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
using SobrietyEngine.Events;
using SobrietyEngine.Events.Input;

namespace SobrietyEngine.Input.Devices
{
    public class GamepadDevice : BaseTranslator, IInputDevice
    {
        private GamePadState curStates = new GamePadState();
        private GamePadState oldStates = new GamePadState();

        #region Dead Zone Data
        private GamePadDeadZone deadZone = GamePadDeadZone.IndependentAxes;
        public GamePadDeadZone DeadZone
        {
            get { return deadZone; }
            set
            {
                deadZone = value;
            }
        }
        /*
        private string deadZoneString;
        private Vector2 deadZoneStringPosition;
        private Vector2 deadZoneStringCenterPosition;
         * */
        #endregion

        public GamepadDevice(IEventRouter er, int gamepadIndex)
            : base(er, true, gamepadIndex)
        {
        }

        Queue<GamepadEnums.GamepadButton> released = new Queue<GamepadEnums.GamepadButton>();
        Queue<GamepadEnums.GamepadButton> pressed = new Queue<GamepadEnums.GamepadButton>();


        private void compareButtons(ButtonState old, ButtonState cur, GamepadEnums.GamepadButton gpb)
        {
            if (cur != old)
            {
                //I think we only care when there's a change in state.
                if (cur == ButtonState.Released)
                {
                    //Raise released event                        
                    released.Enqueue(gpb);
                }
                else
                {
                    //Raise pressed event
                    pressed.Enqueue(gpb);
                }
            }
        }

        override public void Update(GameTime gameTime)
        {
            //save previous state of gamepad
            oldStates = curStates;

            curStates = GamePad.GetState((PlayerIndex)GamepadIndex, deadZone);

            GamePadState gps = curStates;

            //Joystick Data

            //doesnt = 0, or TODO: is in not the deadzone?
            if (oldStates.ThumbSticks.Left != curStates.ThumbSticks.Left)
            {
                IEventData joyEvt = new JoystickMove(this, false, gps.ThumbSticks.Left.X, gps.ThumbSticks.Left.Y);
                EventRouterGameService.RaiseEvent(this, joyEvt);
            }

            if (oldStates.ThumbSticks.Right != curStates.ThumbSticks.Right)
            {
                IEventData joyEvt = new JoystickMove(this, true, gps.ThumbSticks.Right.X, gps.ThumbSticks.Right.Y);
                EventRouterGameService.RaiseEvent(this, joyEvt);
            }

            //Triggers
            if (oldStates.Triggers.Left != curStates.Triggers.Left)
            {
                IEventData joyEvt = new Trigger(this, GamepadEnums.GamepadButton.LeftTrigger, gps.Triggers.Left);
                EventRouterGameService.RaiseEvent(this, joyEvt);
            }

            if (oldStates.Triggers.Right != curStates.Triggers.Right)
            {
                IEventData joyEvt = new Trigger(this, GamepadEnums.GamepadButton.RightTrigger, gps.Triggers.Right);
                EventRouterGameService.RaiseEvent(this, joyEvt);
            }

            //This is ugly and messy and I hate it. WTB a better solution.                
            List<IEventData> buttonEvts = new List<IEventData>();

            compareButtons(oldStates.Buttons.A, curStates.Buttons.A, GamepadEnums.GamepadButton.A);
            compareButtons(oldStates.Buttons.B, curStates.Buttons.B,GamepadEnums.GamepadButton.B);
            compareButtons(oldStates.Buttons.X, curStates.Buttons.X, GamepadEnums.GamepadButton.X);
            compareButtons(oldStates.Buttons.Y, curStates.Buttons.Y, GamepadEnums.GamepadButton.Y);
            compareButtons(oldStates.Buttons.LeftShoulder, curStates.Buttons.LeftShoulder,GamepadEnums.GamepadButton.LeftBumper);
            compareButtons(oldStates.Buttons.RightShoulder, curStates.Buttons.RightShoulder, GamepadEnums.GamepadButton.RightBumper);
            compareButtons(oldStates.Buttons.LeftStick, curStates.Buttons.LeftStick, GamepadEnums.GamepadButton.LeftThumbstickClick);
            compareButtons(oldStates.Buttons.RightStick, curStates.Buttons.RightStick, GamepadEnums.GamepadButton.RightThumbstickClick);
            compareButtons(oldStates.Buttons.Back, curStates.Buttons.Back, GamepadEnums.GamepadButton.Back);
            compareButtons(oldStates.Buttons.Start, curStates.Buttons.Start, GamepadEnums.GamepadButton.Start);
            compareButtons(oldStates.DPad.Up, curStates.DPad.Up, GamepadEnums.GamepadButton.DU);
            compareButtons(oldStates.DPad.Down, curStates.DPad.Down, GamepadEnums.GamepadButton.DD);
            compareButtons(oldStates.DPad.Left, curStates.DPad.Left, GamepadEnums.GamepadButton.DL);
            compareButtons(oldStates.DPad.Right, curStates.DPad.Right, GamepadEnums.GamepadButton.DU);
            compareButtons(oldStates.Buttons.BigButton, curStates.Buttons.BigButton, GamepadEnums.GamepadButton.Guide);

            while (released.Count > 0)
            {
                GamepadEnums.GamepadButton b = (GamepadEnums.GamepadButton)released.Dequeue();
                EventRouterGameService.RaiseEvent(this, new ButtonRelease(this,b));
            }
            while (pressed.Count > 0)
            {
                GamepadEnums.GamepadButton b = (GamepadEnums.GamepadButton)pressed.Dequeue();
                EventRouterGameService.RaiseEvent(this, new ButtonPress(this, b));
            }
        }

        private static bool IsGamepadActive(ref GamePadState gamePadState)
        {
            return (gamePadState.IsConnected &&
                ((gamePadState.Buttons.A == ButtonState.Pressed) ||
                (gamePadState.Buttons.B == ButtonState.Pressed) ||
                (gamePadState.Buttons.X == ButtonState.Pressed) ||
                (gamePadState.Buttons.Y == ButtonState.Pressed) ||
                (gamePadState.Buttons.Start == ButtonState.Pressed) ||
                (gamePadState.Buttons.Back == ButtonState.Pressed) ||
                (gamePadState.Buttons.LeftShoulder == ButtonState.Pressed) ||
                (gamePadState.Buttons.RightShoulder == ButtonState.Pressed) ||
                (gamePadState.Buttons.LeftStick == ButtonState.Pressed) ||
                (gamePadState.Buttons.RightStick == ButtonState.Pressed) ||
                (gamePadState.DPad.Up == ButtonState.Pressed) ||
                (gamePadState.DPad.Left == ButtonState.Pressed) ||
                (gamePadState.DPad.Right == ButtonState.Pressed) ||
                (gamePadState.DPad.Down == ButtonState.Pressed)));
        }

        private void ToggleDeadZone()
        {
            switch (DeadZone)
            {
                case GamePadDeadZone.IndependentAxes:
                    DeadZone = GamePadDeadZone.Circular;
                    break;
                case GamePadDeadZone.Circular:
                    DeadZone = GamePadDeadZone.None;
                    break;
                case GamePadDeadZone.None:
                    DeadZone = GamePadDeadZone.IndependentAxes;
                    break;
            }
        }
    }
}
